iT邦幫忙

2024 iThome 鐵人賽

DAY 25
0
Modern Web

React 學得動嗎系列 第 25

[Day 25] Gym Pro:會員積分系統與課程預約功能

  • 分享至 

  • xImage
  •  

今天,我們要來為系統新增兩個新功能:會員積分系統和課程預約功能,繼續擴充Gym Pro的系統功能完整性。

1. 會員積分系統

會員積分系統可以鼓勵會員更頻繁地使用健身房設施和參加課程。需要設計一個積分獲取和使用的機制。

更新會員模型

首先,我們需要在會員模型中增加積分欄位。在 src/types/Member.ts 中:

export interface Member {
  id: number;
  name: string;
  email: string;
  joinDate: string;
  membershipType: string;
  points: number;  // 新增積分欄位
}

建立積分歷史模型

我們還需要一個模型來記錄積分的獲取和使用歷史。在 src/types/PointHistory.ts 中:

export interface PointHistory {
  id: number;
  memberId: number;
  amount: number;
  type: 'earn' | 'use';
  description: string;
  date: string;
}

實現積分獲取和使用的 API

src/services/pointService.ts 中:

import axios from 'axios';
import { PointHistory } from '../types/PointHistory';

export const earnPoints = async (memberId: number, amount: number, description: string): Promise<PointHistory> => {
  const response = await axios.post('/api/points/earn', { memberId, amount, description });
  return response.data;
};

export const usePoints = async (memberId: number, amount: number, description: string): Promise<PointHistory> => {
  const response = await axios.post('/api/points/use', { memberId, amount, description });
  return response.data;
};

export const getPointHistory = async (memberId: number): Promise<PointHistory[]> => {
  const response = await axios.get(`/api/points/history/${memberId}`);
  return response.data;
};

更新會員詳情頁面

在會員詳情頁面中增加積分信息和積分歷史。在 src/pages/MemberDetail.tsx 中:

import React from 'react';
import { useParams } from 'react-router-dom';
import { useQuery } from '@tanstack/react-query';
import { getMember } from '../services/memberService';
import { getPointHistory } from '../services/pointService';
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card"

const MemberDetail: React.FC = () => {
  const { id } = useParams<{ id: string }>();
  const { data: member } = useQuery(['member', id], () => getMember(id!));
  const { data: pointHistory } = useQuery(['pointHistory', id], () => getPointHistory(Number(id)));

  if (!member) return <div>載入中...</div>;

  return (
    <div className="p-4">
      <h1 className="text-2xl font-bold mb-4">{member.name} 的會員資料</h1>
      <Card className="mb-4">
        <CardHeader>
          <CardTitle>會員資訊</CardTitle>
        </CardHeader>
        <CardContent>
          <p>Email: {member.email}</p>
          <p>加入日期: {member.joinDate}</p>
          <p>會員類型: {member.membershipType}</p>
          <p>積分: {member.points}</p>
        </CardContent>
      </Card>
      <Card>
        <CardHeader>
          <CardTitle>積分歷史</CardTitle>
        </CardHeader>
        <CardContent>
          <ul>
            {pointHistory?.map((history) => (
              <li key={history.id} className="mb-2">
                {history.date}: {history.type === 'earn' ? '獲得' : '使用'} {history.amount} 積分 - {history.description}
              </li>
            ))}
          </ul>
        </CardContent>
      </Card>
    </div>
  );
};

export default MemberDetail;

2. 課程預約功能

課程預約功能可以讓會員輕鬆地預約他們想參加的課程,同時也方便健身房管理課程容量。

建立預約模型

src/types/Reservation.ts 中:

export interface Reservation {
  id: number;
  memberId: number;
  classId: number;
  date: string;
  status: 'confirmed' | 'cancelled' | 'attended';
}

實現預約相關的 API

src/services/reservationService.ts 中:

import axios from 'axios';
import { Reservation } from '../types/Reservation';

export const makeReservation = async (memberId: number, classId: number, date: string): Promise<Reservation> => {
  const response = await axios.post('/api/reservations', { memberId, classId, date });
  return response.data;
};

export const cancelReservation = async (reservationId: number): Promise<void> => {
  await axios.delete(`/api/reservations/${reservationId}`);
};

export const getMemberReservations = async (memberId: number): Promise<Reservation[]> => {
  const response = await axios.get(`/api/reservations/member/${memberId}`);
  return response.data;
};

export const getClassReservations = async (classId: number): Promise<Reservation[]> => {
  const response = await axios.get(`/api/reservations/class/${classId}`);
  return response.data;
};

更新課程詳情頁面

在課程詳情頁面中增加預約功能。在 src/pages/ClassDetail.tsx 中:

import React from 'react';
import { useParams } from 'react-router-dom';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { getClass } from '../services/classService';
import { makeReservation, getClassReservations } from '../services/reservationService';
import { useAuth } from '../contexts/AuthContext';
import { Button } from "@/components/ui/button"
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card"

const ClassDetail: React.FC = () => {
  const { id } = useParams<{ id: string }>();
  const { user } = useAuth();
  const queryClient = useQueryClient();

  const { data: classData } = useQuery(['class', id], () => getClass(id!));
  const { data: reservations } = useQuery(['classReservations', id], () => getClassReservations(Number(id)));

  const reserveMutation = useMutation(makeReservation, {
    onSuccess: () => {
      queryClient.invalidateQueries(['classReservations', id]);
    },
  });

  if (!classData) return <div>載入中...</div>;

  const handleReserve = () => {
    if (user) {
      reserveMutation.mutate({ memberId: user.id, classId: Number(id), date: classData.date });
    }
  };

  return (
    <div className="p-4">
      <h1 className="text-2xl font-bold mb-4">{classData.name}</h1>
      <Card className="mb-4">
        <CardHeader>
          <CardTitle>課程資訊</CardTitle>
        </CardHeader>
        <CardContent>
          <p>教練: {classData.instructor}</p>
          <p>日期: {classData.date}</p>
          <p>時間: {classData.time}</p>
          <p>時長: {classData.duration} 分鐘</p>
          <p>已預約人數: {reservations?.length || 0}</p>
          <Button onClick={handleReserve} disabled={reserveMutation.isLoading}>
            預約課程
          </Button>
        </CardContent>
      </Card>
      <Card>
        <CardHeader>
          <CardTitle>預約名單</CardTitle>
        </CardHeader>
        <CardContent>
          <ul>
            {reservations?.map((reservation) => (
              <li key={reservation.id}>{reservation.memberId}</li>
            ))}
          </ul>
        </CardContent>
      </Card>
    </div>
  );
};

export default ClassDetail;

小結

今天我們為 Gym Pro 系統增加了兩個新功能:

  1. 會員積分系統:允許會員累積和使用積分,提高會員參與度。
  2. 課程預約功能:讓會員可以預約課程,同時方便管理課程容量。

上一篇
[Day 24] Gym Pro:強化安全性與優化用戶體驗
下一篇
[Day 26] Gym Pro:性能優化
系列文
React 學得動嗎30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言